HLE of printf (sys_a0_3f): ------------------------------------------------------------------------- printf не выводит строку. она передает формат и список аргументов функции fprintf. первый параметр fprintf - это устройство (file device). в данном случае используется stdout = 1: int printf(char *fmt, ...) { return fprintf(stdout, fmt, ...); } работа fprintf в общих чертах: ------------------------------- переменные и параметры: fd - file device. устройство для вывода символов. на самом деле не используется - вывод всегда идет в stdout (с помощью putchar()). fmt - строка формата. поддерживаемые в спецификаторах символы: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -, +, *, #, \0, c, d, D, h, i, l, L, n, o, O, p, s, u, U, x, X. полный формат спецификатора: % [флаги] [ширина] [.точность] [{F | N | h | l | L}] тип флаги: - выровнять по левому краю + префикс со знаком # изменяет o, O, x, X * ??? префиксы: h (short int) l,L (long int или double) типы: d,D,i (signed decimal) u,U (unsigned decimal integer) o,O (unsigned octal integer) x,X (unsigned hex integer) c (single character) s (string) p (pointer) n (character count) ... - список аргументов. в контексте будет обозначаться как va_list. printed - количество выведенных символов. fprintf возвращает это значение. buf - вспомогательный буфер (для обработки спецификаторов). hex_tbl - используются для перевода систем счисления. char *hex_tbl_lo[] = "0123456789abcdef"; char *hex_tbl_hi[] = "0123456789ABCDEF"; null - пустая строка (0xBFC0DDB0). null+1 - _ctype_. таблица описания символов ASCII: char _ctype_[128] = { 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x08,0x08,0x08,0x08,0x08,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x18,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, /* 0-9 */ 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x10,0x10,0x10,0x10,0x10,0x10, /* A-Z */ 0x10,0x41,0x41,0x41,0x41,0x41,0x41,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x10,0x10,0x10,0x10,0x10, /* a-z */ 0x10,0x42,0x42,0x42,0x42,0x42,0x42,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x10,0x10,0x10,0x10,0x20 }; примечания: _ctype_ - 0xBFC0DDB1. это таблица на 128 байт, каждый из них описывает соответсвующий символ из набора ASCII. биты: 0 : символ в верхнем регистре 1 : символ в нижнем регистре 2 : символ-цифра 3 : управляющий символ (пробел, табуляция и пр.) 4 : cимвол-буква 5 : непечатный символ 6 : символ-шестнадцатеричная цифра 7 : не используется более подробно про это можно посмотреть в "PSYQ\INCLUDE\CTYPE.H". /* общий вид */ int fprintf(int fd, char *fmt, ...) { int printed = 0; char c; if(fmt == NULL) return 0; while(1) { c = *fmt; if(c == 0) return printed; if(a != '%') { putchar(c); fmt++; } else { /* обработка % */ fmt++; switch(*fmt) { case 0: return printed; case ... break; case ... break; . . . default: ОШИБКА. } fmt++; } } } примечания: - с вероятностью в 99% можно сказать, что fprintf написана на ассемблере. иначе использовалась полная оптимизация (ключ -O3). - как можно заметить, в участке кода "if(a != '%') {...}" не учитывается количество выведенных символов, то есть там не хватает строки: "printed++;". поэтому fprintf возвращает количество символов, выведенных только у спецификаций. например printf("Hello, %i", 123) возвратит не 10, а только 3. обработка и вывод '%': ----------------------- так как понять общую схему анализа и вывода спецификаций (%) довольно трудно, если вообще возможно, то каждый спецификатор будет разбираться по очереди. причем исходный код уже ориентирован на HLE. поэтому отпадает необходимость в hex_tbl, null и пр. алгоритм: 1. обычные символы мы печатаем как и в исходной функции, сохраняя глюк с printed. 2. когда встречается спецификатор, мы выбираем его весь из fmt и помещаем в специальный буфер spec[]. 3. в переменную arg попадает следующий аргумент из списка, причем если это '%s', то мы транслируем arg, который является в данном случае указателем на строку. 4. используем для форматирования строки: printed += sprintf(buf, spec, arg); 5. выводим buf с помощью putchar(). /* псевдоисходник */ int fprintf(int fd, char *fmt, ...) { int printed = 0; char c, buf[0x200], spec[64]; u_long arg; if(fmt == NULL) return 0; while(1) { c = *fmt; if(c == 0) return printed; if(a != '%') { putchar(c); fmt++; continue; } else { /* обработка % */ /* вырезать спецификатор в spec[] из fmt */ fmt += cut_spec(spec, fmt) - 1; if(*fmt == 0) return printed; /* получить arg, и транслирвать, если "%s" */ arg = *va_list++; if(*fmt == 's') TRANSLATE(arg); fmt++; /* форматировать строку */ printed += sprintf(buf, spec, arg); /* вывести buf[] на stdout */ for(i=0; i